#include <stdio.h>
#include <string.h>
#include "TSintactico.h"

typedef enum {
	TOKEN_STRING, /*cadena de caracteres*/
	TOKEN_NUMERO, /*numero en punto flotante*/
	TOKEN_OBJETO_EMPIEZA, /*inicio de objeto*/
	TOKEN_OBJETO_TERMINA, /*fin de objeto*/
	TOKEN_ARRAY_EMPIEZA, /*inicio de array*/
	TOKEN_ARRAY_TERMINA, /*fin de array*/
	TOKEN_COMA, /*coma*/
	TOKEN_DOSPUNTOS, /*dos puntos*/
	TOKEN_TRUE, TOKEN_FALSE, TOKEN_NULL
} TipoToken;

typedef struct _tLexico {
	Token token;
	int tokenListo;
	TSintactico* as;
	TipoErrores ultimo_error;
} TLexico;

/*              Funciones Internas				    */

void TLexico_setUltimoError(TLexico* al, int codigo, char descripcion[MAX_DATO]) {
	al->ultimo_error.codigo = codigo;
	strcpy(al->ultimo_error.descripcion, descripcion);
}

int vaciar_Token(TLexico* al) {
	al->token.tipo = -1;
	al->token.pos_dato = 0;
	al->tokenListo = 0;
	return 0;
}

int analizar_Caracter(TLexico* al, char c) {
	int aux = 0;
	char true[] = { "true" };
	char false[] = { "false" };
	char null[] = { "null" };
	/*Si ya se empezo a escribir algo (pos_dato != 0) y es un String entonces...*/
	if (al->token.pos_dato != 0 && al->token.tipo == TOKEN_STRING) {
		if (('0' <= c && c <= '9') || ('A' <= c && c <= 'Z') || ('a' <= c && c
				<= 'z') || (c == ' ') || (c == '"')) { /*Verifico que sea un caracter alfa-numerico o espacio*/
			al->token.dato[al->token.pos_dato] = c;
			if (al->token.pos_dato < MAX_DATO) {
				al->token.pos_dato++;
			} else {
				aux = 1;
				TLexico_setUltimoError(al, 110,
						"Se ha superado la cantidad maxima de caracteres permitido (max: 256).");
			}
			if (c == '"') { /*Verifico que si es el fin de STRING*/
				al->token.dato[al->token.pos_dato] = '\0';
				al->tokenListo = 1;
			}
		} else {
			aux = 1;
			TLexico_setUltimoError(
					al,
					120,
					"El caracter ingresado no es alfa-numerico, por lo que no puede formar parte de un STRING.");
		}
		/*Si el caracter es un numero, entonces... */
	} else if (c >= '0' && c <= '9') {
		al->token.tipo = TOKEN_NUMERO;
		al->token.dato[al->token.pos_dato] = c;
		if (al->token.pos_dato < MAX_DATO) {
			al->token.pos_dato++;
		} else {
			aux = 1;
			TLexico_setUltimoError(al, 110,
					"Se ha superado la cantidad maxima de caracteres permitido (max: 256).");
		}
		/*Si el caracter NO es un numero, pero el token que se "venia" formando si lo era,
		 *entonces finaliza dicho token, lo manda y luego continua analizando el caracter */
	} else if (al->token.tipo == TOKEN_NUMERO) {
		if (c == '.') {
			/*si es numero decimal, lo toma como entero*/
		} else{
			al->tokenListo = 1;
			al->token.dato[al->token.pos_dato] = '\0';
			if (TSintactico_PushToken(al->as, &(al->token)) == 0) {
				vaciar_Token(al);
			}
		}
	} else if ((al->token.pos_dato != 0) && (al->token.tipo == TOKEN_TRUE
			|| al->token.tipo == TOKEN_FALSE || al->token.tipo == TOKEN_NULL)) {
		al->token.dato[al->token.pos_dato] = c;
		switch (al->token.tipo) {
		/*Este switch va analizando si se forma alguno de los tres tokens (true, false, null)
		 * en caso de que no concuerde alguna letra da error*/
		case (TOKEN_TRUE):
			if (al->token.dato[al->token.pos_dato] != true[al->token.pos_dato]) {
				aux = 1;
			} else {
				al->token.pos_dato++;
				if (al->token.pos_dato == 4)
					al->tokenListo = 1;
			}

			break;
		case (TOKEN_FALSE):
			if (al->token.dato[al->token.pos_dato] != false[al->token.pos_dato]) {
				aux = 1;
			} else {
				al->token.pos_dato++;
				if (al->token.pos_dato == 5)
					al->tokenListo = 1;
			}
			break;
		case (TOKEN_NULL):
			if (al->token.dato[al->token.pos_dato] != null[al->token.pos_dato]) {
				aux = 1;
			} else {
				al->token.pos_dato++;
				if (al->token.pos_dato == 4)
					al->tokenListo = 1;
			}
			break;
		}
	}
	if (al->token.tipo != TOKEN_STRING && c <= 32) {
	} else if (al->token.tipo == -1)
		switch (c) {
		case ('{'):
			al->token.tipo = TOKEN_OBJETO_EMPIEZA;
			al->tokenListo = 1;
			break;
		case ('}'):
			al->token.tipo = TOKEN_OBJETO_TERMINA;
			al->tokenListo = 1;
			break;
		case ('['):
			al->token.tipo = TOKEN_ARRAY_EMPIEZA;
			al->tokenListo = 1;
			break;
		case (']'):
			al->token.tipo = TOKEN_ARRAY_TERMINA;
			al->tokenListo = 1;
			break;
		case (','):
			al->token.tipo = TOKEN_COMA;
			al->tokenListo = 1;
			break;
		case (':'):
			al->token.tipo = TOKEN_DOSPUNTOS;
			al->tokenListo = 1;
			break;
		case ('"'):
			al->token.tipo = TOKEN_STRING;
			al->token.dato[al->token.pos_dato] = c;
			al->token.pos_dato++;
			break;
		case ('t'):
			al->token.tipo = TOKEN_TRUE;
			al->token.dato[al->token.pos_dato] = c;
			al->token.pos_dato++;
			break;
		case ('f'):
			al->token.tipo = TOKEN_FALSE;
			al->token.dato[al->token.pos_dato] = c;
			al->token.pos_dato++;
			break;
		case ('n'):
			al->token.tipo = TOKEN_NULL;
			al->token.dato[al->token.pos_dato] = c;
			al->token.pos_dato++;
			break;
		default:
			TLexico_setUltimoError(al, 10,
					"El caracter ingresado no se corresponde con la escritura valida de un JSON.");
			aux = 1;
		}

	return aux;
}

/*				Funciones Primitivas                */

int TLexico_Crear(TLexico* al) {
	al->token.tipo = -1;
	al->token.pos_dato = 0;
	al->ultimo_error.codigo = 0;
	strcpy(al->ultimo_error.descripcion, "No se han producido errores");
	return 0;
}

int TLexico_setAnalizadorSintactico(TLexico* al, TSintactico* as) {
	al->as = as;
	return 0;
}

int TLexico_PushChar(TLexico* al, char c) {
	int aux = 0;

	if (analizar_Caracter(al, c) != 0) {
		aux = (int) 'l';
	}
	if (aux == 0 && al->tokenListo == 1) {
		if (TSintactico_PushToken(al->as, &(al->token)) == 0) {
			vaciar_Token(al);
		} else {
			aux = (int) 's';
		}
	}

	return aux;
}

int TLexico_terminarFlujo(TLexico* al) {

	al->token.pos_dato = 0;
	al->token.tipo = -1;
	al->tokenListo = 0;
	al->ultimo_error.codigo = 0;
	strcpy(al->ultimo_error.descripcion, "No se han producido errores");
	return 0;
}

int TLexico_Destruir(TLexico* al) {
	return 0;
}

int TLexico_getUltimoError(TLexico* al, int* codigo, char* mensaje) {
	int i;

	*codigo = al->ultimo_error.codigo;
	for (i = 0; i <= MAX_DATO && al->ultimo_error.descripcion[i] != '\0'; i++) {
		mensaje[i] = al->ultimo_error.descripcion[i];
	}
	mensaje[i] = '\0';
	return 0;
}

